package cn.achao.csr.chat.service.impl;

import cn.achao.csr.api.model.dto.RoomBaseInfoDTO;
import cn.achao.csr.api.model.entity.*;
import cn.achao.csr.api.model.enums.ChatActiveStatusEnum;
import cn.achao.csr.api.model.enums.GroupRoleAPPEnum;
import cn.achao.csr.api.model.enums.RoomTypeEnum;
import cn.achao.csr.api.model.req.MemberReq;
import cn.achao.csr.api.model.resp.ChatMemberResp;
import cn.achao.csr.api.model.resp.ChatRoomResp;
import cn.achao.csr.api.model.resp.MemberResp;
import cn.achao.csr.base.constant.SecurityConstants;
import cn.achao.csr.base.constant.enums.SysSourceEnum;
import cn.achao.csr.base.exception.BusinessException;
import cn.achao.csr.base.resp.CursorPageBaseResp;
import cn.achao.csr.base.resp.R;
import cn.achao.csr.base.utils.ThrowUtils;
import cn.achao.csr.chat.core.build.ChatAdapter;
import cn.achao.csr.chat.core.build.MemberAdapter;
import cn.achao.csr.chat.dao.*;
import cn.achao.csr.chat.service.ChatRoomService;
import cn.achao.csr.chat.service.cache.HotRoomCacheService;
import cn.achao.csr.chat.service.cache.OnlineUserCacheService;
import cn.achao.csr.chat.service.strategy.msg.AbstractMsgHandler;
import cn.achao.csr.chat.service.strategy.msg.MsgHandlerFactory;
import cn.achao.csr.chat.utils.ChatMemberHelperUtil;
import cn.achao.csr.mybatis.req.MPBaseCursorPageReq;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Pair;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import lombok.extern.slf4j.Slf4j;
import lombok.RequiredArgsConstructor;

import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * (ChatRoom)表服务实现类
 *
 * @author achao
 * @since 2025-02-06 21:42:43
 */
@Slf4j
@Service("chatRoomService")
@RequiredArgsConstructor
public class ChatRoomServiceImpl implements ChatRoomService {

    private final ChatUserDao userDao;
    private final ChatRoomDao roomDao;
    private final ChatMsgDao messageDao;
    private final ChatContactDao contactDao;
    private final ChatRoomGroupDao roomGroupDao;
    private final ChatRoomFriendDao roomFriendDao;
    private final ChatGroupMemberDao groupMemberDao;

    private final HotRoomCacheService hotRoomCache;
    private final OnlineUserCacheService onlineUserCache;


    @Override
    public CursorPageBaseResp<ChatRoomResp> getContactPage(MPBaseCursorPageReq request, Long uid) {
        // 查出用户要展示的会话列表
        CursorPageBaseResp<Long> page;
        if (Objects.nonNull(uid)) {
            Double hotEnd = getCursorOrNull(request.getCursor());
            Double hotStart = null;
            // 用户基础会话
            CursorPageBaseResp<ChatContact> contactPage = contactDao.getContactPage(uid, request);
            List<Long> baseRoomIds = contactPage.getList().stream().map(ChatContact::getRoomId).collect(Collectors.toList());
            if (!contactPage.getIsLast()) {
                hotStart = getCursorOrNull(contactPage.getCursor());
            }
            // 热门房间
            Set<ZSetOperations.TypedTuple<String>> typedTuples = hotRoomCache.getRoomRange(hotStart, hotEnd);
            List<Long> hotRoomIds = typedTuples.stream().map(ZSetOperations.TypedTuple::getValue).filter(Objects::nonNull).map(Long::parseLong).collect(Collectors.toList());
            baseRoomIds.addAll(hotRoomIds);
            // 基础会话和热门房间合并
            page = CursorPageBaseResp.init(contactPage, baseRoomIds);
        } else {// 用户未登录，只查全局房间
            CursorPageBaseResp<Pair<Long, Double>> roomCursorPage = hotRoomCache.getRoomCursorPage(request);
            List<Long> roomIds = roomCursorPage.getList().stream().map(Pair::getKey).collect(Collectors.toList());
            page = CursorPageBaseResp.init(roomCursorPage, roomIds);
        }
        // 最后组装会话信息（名称，头像，未读数等）
        List<ChatRoomResp> result = buildContactResp(uid, page.getList());
        return CursorPageBaseResp.init(page, result);
    }

    @Override
    public MemberResp roomDetail(Long roomId, Long userId) {
        ChatRoomGroup roomGroup = roomGroupDao.getByRoomId(roomId);
        ChatRoom room = roomDao.getById(roomId);
        ThrowUtils.throwIf(room == null || roomGroup == null,
                BusinessException.fail("群聊不存在", SysSourceEnum.CSR_CHAT.getSysName()));
        Long onlineNum;
        if (room.isHotRoom()) {
            onlineNum = onlineUserCache.getOnlineNum();
        } else {
            List<Long> memberUidList = groupMemberDao.getMemberUidList(roomGroup.getId());
            onlineNum = userDao.getOnlineCount(memberUidList).longValue();
        }

        GroupRoleAPPEnum groupRole = getGroupRole(userId, roomGroup, room);
        return MemberResp.builder()
                .avatar(roomGroup.getRoomAvatar())
                .roomId(roomId)
                .groupName(roomGroup.getRoomName())
                .onlineNum(onlineNum)
                .role(groupRole.getType())
                .build();
    }

    @Override
    public CursorPageBaseResp<ChatMemberResp> getMemberPage(MemberReq req) {
        ChatRoom room = roomDao.getById(req.getRoomId());
        ThrowUtils.throwIf(room == null,
                BusinessException.fail("群聊不存在", SysSourceEnum.CSR_CHAT.getSysName()));
        List<Long> memberUidList;
        if (room.isHotRoom()) {// 全员群展示所有用户
            memberUidList = null;
        } else {// 只展示房间内的群成员
            ChatRoomGroup roomGroup = roomGroupDao.getByRoomId(req.getRoomId());
            memberUidList = groupMemberDao.getMemberUidList(roomGroup.getId());
        }
        return getMemberPage(memberUidList, req);
    }


    private CursorPageBaseResp<ChatMemberResp> getMemberPage(List<Long> memberUidList, MemberReq req) {
        Pair<ChatActiveStatusEnum, String> pair = ChatMemberHelperUtil.getCursorPair(req.getCursor());
        ChatActiveStatusEnum activeStatusEnum = pair.getKey();
        String timeCursor = pair.getValue();
        List<ChatMemberResp> resultList = new ArrayList<>();//最终列表
        Boolean isLast = Boolean.FALSE;
        if (activeStatusEnum == ChatActiveStatusEnum.ONLINE) {//在线列表
            CursorPageBaseResp<ChatUser> cursorPage = userDao.getCursorPage(memberUidList, new MPBaseCursorPageReq(req.getPageSize(), timeCursor), ChatActiveStatusEnum.ONLINE);
            resultList.addAll(MemberAdapter.buildMember(cursorPage.getList()));//添加在线列表
            if (cursorPage.getIsLast()) {//如果是最后一页,从离线列表再补点数据
                activeStatusEnum = ChatActiveStatusEnum.OFFLINE;
                Integer leftSize = req.getPageSize() - cursorPage.getList().size();
                cursorPage = userDao.getCursorPage(memberUidList, new MPBaseCursorPageReq(leftSize, null), ChatActiveStatusEnum.OFFLINE);
                resultList.addAll(MemberAdapter.buildMember(cursorPage.getList()));//添加离线线列表
            }
            timeCursor = cursorPage.getCursor();
            isLast = cursorPage.getIsLast();
        } else if (activeStatusEnum == ChatActiveStatusEnum.OFFLINE) {//离线列表
            CursorPageBaseResp<ChatUser> cursorPage = userDao.getCursorPage(memberUidList, new MPBaseCursorPageReq(req.getPageSize(), timeCursor), ChatActiveStatusEnum.OFFLINE);
            resultList.addAll(MemberAdapter.buildMember(cursorPage.getList()));//添加离线线列表
            timeCursor = cursorPage.getCursor();
            isLast = cursorPage.getIsLast();
        }
        // 获取群成员角色ID
        List<Long> uidList = resultList.stream().map(ChatMemberResp::getUid).collect(Collectors.toList());
        ChatRoomGroup roomGroup = roomGroupDao.getByRoomId(req.getRoomId());
        Map<Long, Integer> uidMapRole = groupMemberDao.getMemberMapRole(roomGroup.getId(), uidList);
        resultList.forEach(member -> member.setRoleId(uidMapRole.get(member.getUid())));
        //组装结果
        return new CursorPageBaseResp<>(ChatMemberHelperUtil.generateCursor(activeStatusEnum, timeCursor), isLast, resultList);
    }


    private GroupRoleAPPEnum getGroupRole(Long uid, ChatRoomGroup roomGroup, ChatRoom room) {
        ChatGroupMember member = Objects.isNull(uid) ? null : groupMemberDao.getMember(roomGroup.getId(), uid);
        if (Objects.nonNull(member)) {
            return GroupRoleAPPEnum.of(member.getGroupRole());
        } else if (room.isHotRoom()) {
            return GroupRoleAPPEnum.MEMBER;
        } else {
            return GroupRoleAPPEnum.REMOVE;
        }
    }

    private Double getCursorOrNull(String cursor) {
        return Optional.ofNullable(cursor).map(Double::parseDouble).orElse(null);
    }

    @NotNull
    private List<ChatRoomResp> buildContactResp(Long uid, List<Long> roomIds) {
        // 表情和头像
        Map<Long, RoomBaseInfoDTO> roomBaseInfoMap = getRoomBaseInfoMap(roomIds, uid);
        // 最后一条消息
        List<Long> msgIds = roomBaseInfoMap.values().stream().map(RoomBaseInfoDTO::getLastMsgId).collect(Collectors.toList());
        List<ChatMsg> messages = CollectionUtil.isEmpty(msgIds) ? new ArrayList<>() : messageDao.listByIds(msgIds);
        Map<Long, ChatMsg> msgMap = messages.stream().collect(Collectors.toMap(ChatMsg::getId, Function.identity()));
        Map<Long, ChatUser> lastMsgUidMap = userDao.getUserListByIds(messages.stream().map(ChatMsg::getFromUid).collect(Collectors.toList()));
        // 消息未读数
        Map<Long, Integer> unReadCountMap = getUnReadCountMap(uid, roomIds);
        return roomBaseInfoMap.values().stream().map(room -> {
                    ChatRoomResp resp = new ChatRoomResp();
                    RoomBaseInfoDTO roomBaseInfo = roomBaseInfoMap.get(room.getRoomId());
                    resp.setAvatar(roomBaseInfo.getAvatar());
                    resp.setRoomId(room.getRoomId());
                    resp.setActiveTime(room.getActiveTime());
                    resp.setHot_Flag(roomBaseInfo.getHotFlag());
                    resp.setType(roomBaseInfo.getType());
                    resp.setName(roomBaseInfo.getName());
                    ChatMsg message = msgMap.get(room.getLastMsgId());
                    if (Objects.nonNull(message)) {
                        AbstractMsgHandler strategyNoNull = MsgHandlerFactory.getStrategyNoNull(message.getType());
                        resp.setText(lastMsgUidMap.get(message.getFromUid()).getNickName() + ":" + strategyNoNull.showContactMsg(message));
                    }
                    resp.setUnreadCount(unReadCountMap.getOrDefault(room.getRoomId(), 0));
                    return resp;
                }).sorted(Comparator.comparing(ChatRoomResp::getActiveTime).reversed())
                .collect(Collectors.toList());
    }

    /**
     * 获取未读数
     */
    private Map<Long, Integer> getUnReadCountMap(Long uid, List<Long> roomIds) {
        if (Objects.isNull(uid)) {
            return new HashMap<>();
        }
        List<ChatContact> contacts = contactDao.getByRoomIds(roomIds, uid);
        return contacts.parallelStream()
                .map(contact -> Pair.of(contact.getRoomId(), messageDao.getUnReadCount(contact.getRoomId(), contact.getReadTime())))
                .collect(Collectors.toMap(Pair::getKey, Pair::getValue));
    }

    private Map<Long, RoomBaseInfoDTO> getRoomBaseInfoMap(List<Long> roomIds, Long uid) {
        Map<Long, ChatRoom> roomMap = roomDao.getBatchByIds(roomIds);
        // 房间根据好友和群组类型分组
        Map<Integer, List<Long>> groupRoomIdMap = roomMap.values().stream().collect(Collectors.groupingBy(ChatRoom::getRoomType,
                Collectors.mapping(ChatRoom::getId, Collectors.toList())));
        // 获取群组信息
        List<Long> groupRoomId = groupRoomIdMap.get(RoomTypeEnum.GROUP.getType());
        Map<Long, ChatRoomGroup> roomInfoBatch = roomGroupDao.getBatchByRoomIds(groupRoomId);
        // 获取好友信息
        List<Long> friendRoomId = groupRoomIdMap.get(RoomTypeEnum.FRIEND.getType());
        Map<Long, ChatUser> friendRoomMap = getFriendRoomMap(friendRoomId, uid);

        return roomMap.values().stream().map(room -> {
            RoomBaseInfoDTO roomBaseInfo = new RoomBaseInfoDTO();
            roomBaseInfo.setRoomId(room.getId());
            roomBaseInfo.setType(room.getRoomType());
            roomBaseInfo.setHotFlag(room.getHotFlag());
            roomBaseInfo.setLastMsgId(room.getLastMsgId());
            roomBaseInfo.setActiveTime(room.getActiveTime());
            if (RoomTypeEnum.of(room.getRoomType()) == RoomTypeEnum.GROUP) {
                ChatRoomGroup roomGroup = roomInfoBatch.get(room.getId());
                roomBaseInfo.setName(roomGroup.getRoomName());
                roomBaseInfo.setAvatar(roomGroup.getRoomAvatar());
            } else if (RoomTypeEnum.of(room.getRoomType()) == RoomTypeEnum.FRIEND) {
                ChatUser user = friendRoomMap.get(room.getId());
                roomBaseInfo.setName(user.getNickName());
                roomBaseInfo.setAvatar(user.getAvatarUrl());
            }
            return roomBaseInfo;
        }).collect(Collectors.toMap(RoomBaseInfoDTO::getRoomId, Function.identity()));
    }

    private Map<Long, ChatUser> getFriendRoomMap(List<Long> roomIds, Long uid) {
        if (CollectionUtil.isEmpty(roomIds)) {
            return new HashMap<>();
        }
        Map<Long, ChatRoomFriend> roomFriendMap = roomFriendDao.getBatchByRoomIds(roomIds);
        Set<Long> friendUidSet = ChatAdapter.getFriendUidSet(roomFriendMap.values(), uid);
        Map<Long, ChatUser> userBatch = userDao.getUserListByIds(new ArrayList<>(friendUidSet));
        return roomFriendMap.values()
                .stream()
                .collect(Collectors.toMap(ChatRoomFriend::getRoomId, roomFriend -> {
                    Long friendUid = ChatAdapter.getFriendUid(roomFriend, uid);
                    return userBatch.get(friendUid);
                }));
    }
}
